静态文档站发布:GitHub Pages 与 Actions
包发到 npm 只是交付的一半,使用者还要能「看见」怎么用。文档站一直跑在本地 localhost:5173,得发布到公网。这节用 GitHub Pages + Actions 把 VitePress 文档站免费发布上线,并把「依赖本地 mock server 的页面」在构建期干净剔除。整个 CI/CD 配置同样交给 AI 一次跑通。
选 GitHub Pages,以及一个前置问题
发布静态文档站的方式很多:公网服务器、对象存储 + CDN、GitHub Pages……课程项目选 GitHub Pages:免费、与代码仓库一体、push 即部署。
但有个前置矛盾:文档站里有一批页面(Mock 演示、向量检索演示、接口文档等)依赖 pnpm dev 同时启动的本地 dev-server(8787 端口),而 GitHub Pages 只支持静态文件、不支持常驻进程。取舍:
- 静态部分照发:指南、组件文档(message、prompt-input、attachments 等)、playground、主题配置器——这些不需要后端
- mock 相关页面在构建期完全剔除——不是简单隐藏入口。只藏入口不行:VitePress 死链检查会让构建失败,产物里还会残留指向不存在页面的死代码
剔除方式用构建期环境变量开关:不改业务代码逻辑,加一个条件分支;CI 注入 DOCS_TARGET=pages 就剔除依赖后端的页面,本地开发完全不受影响。
GitHub Fine-grained PAT:给 AI 操作仓库的权限
和 npm token 同一个思路:第三方平台(这次是 GitHub)发一个 API token,AI 拿 token 就能建 workflow、推分支、触发部署。
创建步骤:GitHub 头像 → Settings → 左侧最底 Developer settings → Personal access tokens → Fine-grained tokens → Generate new token:
| 配置项 | 填法 |
|---|---|
| Token name / Expiration | 如 docs-deploy;演示用 7 天 |
| Repository access | 只限当前仓库(Least privilege) |
| Permissions → Contents | Read and write(读写仓库内容,最关键的一项) |
| Permissions → Actions | Read and write(触发与查看 workflow) |
| Permissions → Codespaces / Deployments 等 | 按提示能勾的都勾上,部署链路会用到 |
Generate 后复制,只显示一次。
交给 AI:一次 brainstorm 定三个决策
提示词要点:
> 这是当前项目的 GitHub token(Fine-grained,Contents/Actions 读写,7 天有效)。
> 请规划并实现:把 VitePress 文档站发布到 GitHub Pages,
> 需要隐藏依赖本地 mock server 的演示页面(静态展示用不到),
> 并说明需要我配合做哪些一次性仓库配置。
text
AI 先 brainstorm 再动手,会回来确认三个决策(都选它推荐的):
| 决策点 | 选项 | 选型与理由 |
|---|---|---|
| 部署触发 | 仅 master / 多分支 | 仅 master。双分支同时部署会互相覆盖 Pages 站点;push 到 master 自动触发 |
| 隐藏 mock 页面 | 构建期剔除(环境变量开关)/ 运行时隐藏入口 | 构建期剔除。不污染业务代码,一个条件判断关掉两个入口,产物更干净 |
| 部署方案 | 官方 Actions / gh-pages 分支第三方 action | 官方 Actions(configure-pages + upload-pages-artifact + deploy-pages):原子部署、artifact 带校验。gh-pages 方案要单独推分支、配额外 PAT 或 deploy key、非原子部署、噪音大 |
两点经验:
- 需求描述清楚 + AI 给的实施计划明确时,直接让它开干,不必每次都写 spec 文档;这次还要它自己列出「需要人工配合的一次性配置」清单
- 这类「写配置 → 提交 → 触发 CI → 看结果」的活是串行的,单 agent 直接干完;只有可并行的多功能开发才值得开 subagent 团队
构建期剔除:DOCS_TARGET 开关(仓库真实实现)
// packages/docs/.vitepress/config.ts(节选)
// GitHub Pages 发布构建开关:DOCS_TARGET=pages 时剔除依赖本地 dev-server 的页面
const isPages = process.env.DOCS_TARGET === 'pages'
export default defineConfig({
// 项目站点部署在 /learn-ai/ 子路径下,base 必须跟着走(BASE_PATH 由 CI 注入)
base: process.env.BASE_PATH || '/',
// 剔除依赖 dev-server 的页面:这些 md 不再参与构建
srcExclude: isPages
? [
'vector-search-demo.md',
'mock-server-demo.md',
'multi-agent-demo.md',
'workflow-demo.md',
'genui-demo.md',
'mock-api.md',
]
: [],
// 死链检查是静态扫描,被剔除页面的入口链接要精确放行,否则构建失败
ignoreDeadLinks: [
/^https?:\/\/localhost(?::\d+)?/,
...(isPages ? ['/vector-search-demo', '/mock-server-demo', '/multi-agent-demo', '/workflow-demo', '/genui-demo', '/mock-api'] : []),
],
vite: {
define: {
// 构建期注入字面量:客户端组件(如 MockOnly 包裹的入口)据此做常量折叠
'import.meta.env.DOCS_TARGET': JSON.stringify(process.env.DOCS_TARGET ?? ''),
},
},
})
typescript
三个动作各管一段:srcExclude 让 mock 页面不构建;ignoreDeadLinks 让残留链接不报错;define 把开关注入客户端,导航里 Mock 相关的入口组件按 import.meta.env.DOCS_TARGET === 'pages' 折叠掉——这正是 brainstorm 里说的「一个环境变量隐藏两个入口」。
完整部署 workflow(仓库真实文件)
# .github/workflows/docs-deploy.yml
name: Deploy Docs
on:
push:
branches: [master] # 仅 master 触发,双分支会互相覆盖
workflow_dispatch: # 支持手动触发,方便补部署
permissions:
contents: read
pages: write # 创建 Pages 部署
id-token: write # OIDC 令牌,验证部署来源(deploy-pages 必需)
concurrency:
group: github-pages
cancel-in-progress: false # 同一时刻只有一个 Pages 部署,后来的排队不抢跑
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11.21.0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'pnpm'
- name: Install dependencies
run: corepack pnpm install --frozen-lockfile
- name: Build docs
run: corepack pnpm -C packages/docs build
env:
DOCS_TARGET: pages # 构建期剔除 mock 页面
BASE_PATH: /learn-ai/ # 项目 Pages 的子路径
- name: Setup Pages
uses: actions/configure-pages@v5
with:
enablement: true # 首次自动开启仓库的 Pages 功能
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: packages/docs/.vitepress/dist
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages # 部署进 github-pages 环境,带部署记录
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4 # 当前主版本;要求 pages:write + id-token:write
yaml
要点解读:
- permissions 三件套是官方 deploy-pages 的硬性要求:
pages: write调 Pages API 创建部署,id-token: write请求 OIDC JWT 验证这次部署确实来自允许的分支/工作流 - 两个 job 分离:build 只管产出 artifact(
upload-pages-artifact打 tar 包),deploy 消费 artifact——这是官方推荐结构,部署失败可以直接重跑 deploy 不重新构建 - concurrency 让 Pages 部署串行排队,避免连续 push 时后一次构建覆盖前一次的半成品部署
configure-pages的enablement: true会通过 API 自动启用仓库 Pages,省一步网页操作(当前主版本已是 v6,运行时 Node 24;仓库里锁的是 v5,同样可用)
仓库一次性配置:首次部署 403 的坑
AI 交付后第一次跑 CI 失败,报账号缺权限。两个排查点(都是一次性配置):
- Settings → Pages → Build and deployment → Source 必须选 GitHub Actions。默认是「Deploy from a branch」,不切过来 deploy-pages 没有部署入口
- 若曾收紧过 Settings → Actions → General → Workflow permissions,需要允许 Read and write(上面的 workflow 已显式声明 permissions 时可不动全局设置)
改完重新跑 workflow 即绿。
部署验证清单
打开 https://<user>.github.io/learn-ai/ 逐项过:
- 指南 → 快速开始、安装、主题定制等静态页正常
- 组件页(message、prompt-input、attachments、image 等)正常渲染
- playground、主题配置器正常(纯前端交互)
- 文档内 GitHub 仓库链接正常跳转
- 导航中不再出现 Mock 演示 / 接口文档入口,直接访问对应路径也是 404(构建期剔除生效)
一个使用预期:github.io 在大陆访问时好时坏,国内用户为主的站点可以绑定自定义域名走 CDN,或改用 05 节的 Cloudflare/Vercel 方案。
小结
- GitHub Pages 只发静态:mock 相关页面用
DOCS_TARGET=pages构建期剔除(srcExclude + ignoreDeadLinks + define 三件套),本地开发不受影响 - 官方 Actions 部署三步:configure-pages(enablement 自动开 Pages)→ upload-pages-artifact → deploy-pages@v4;permissions 三件套(contents: read / pages: write / id-token: write)是硬要求
- 首次部署 403 的两个一次性配置:Pages Source 选 GitHub Actions、Workflow permissions 允许读写
- 触发只留 master(双分支互相覆盖);Fine-grained PAT 最小权限只授当前仓库的 Contents/Actions
- 「写配置→提交→看 CI」是串行任务,单 agent 直干;可并行的多功能开发才开 subagent 团队
文档站上线了,但 mock server 还没有线上归宿——GitHub Pages 跑不了常驻进程。下一节部署到 Vercel,并对比 Cloudflare Workers 等方案。
↑